package com.client.koshercook.server;

import java.io.UnsupportedEncodingException;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;

import com.client.koshercook.client.ClientRequest;
import com.client.koshercook.client.ClientResponse;
import com.client.koshercook.client.RecipeDetails;
import com.client.koshercook.client.GreetingService;
import com.google.appengine.api.users.User;  
import com.google.appengine.api.users.UserService;  
import com.google.appengine.api.users.UserServiceFactory;
import com.google.appengine.api.datastore.Key;
import com.google.appengine.api.datastore.KeyFactory;
import com.google.gwt.user.server.rpc.RemoteServiceServlet;


import java.util.Properties;
import javax.mail.Message;
import javax.mail.MessagingException;
import javax.mail.Multipart;
import javax.mail.Session;
import javax.mail.Transport;
import javax.mail.internet.AddressException;
import javax.mail.internet.InternetAddress;
import javax.mail.internet.MimeBodyPart;
import javax.mail.internet.MimeMessage;
import javax.mail.internet.MimeMultipart;
import javax.mail.internet.MimeUtility;

/**
 * The server side implementation of the RPC service.
 */
@SuppressWarnings("serial")
public class GreetingServiceImpl extends RemoteServiceServlet implements GreetingService {

    public ClientResponse greetServer(ClientRequest request) throws IllegalArgumentException{ 
        UserService userService = UserServiceFactory.getUserService();  
        User user = userService.getCurrentUser(); 
        Accounts acc = null; 
        if(user!=null) acc = DataStore.getAccountOfUser(user); 

        System.out.print("input from client = "); 
        int numOfIngs = 0; 
        for(String ingName : request){ 
                System.out.print(ingName+", "); 
                numOfIngs++; 
        } 
        System.out.println(" (total of "+numOfIngs+" ingredients)");

        // GENERATING RESPONSE FOR CLIENT  
        //creating list of Ingredients keys(instead of clien'ts list of strings) 
        List<Key> ings = Strings2Ingredients(request,acc); 
        if(ings==null) return null; 
        else if(ings.size()==0) return null; 
        List<Key> mustIngs = getMustIngs(request, ings); 
         
        //getting the recipes 
        short kosher; 
        if(request.isKosherMeat()) 
                kosher = KosherType.MEAT; 
        else if(request.isKosherDairy()) 
                kosher = KosherType.DAIRY; 
        else if(request.isKosherParve()) 
                kosher = KosherType.PARVE; 
        else // request.isKosherAll() 
                kosher = KosherType.ALL; 
        List<KeyWithWeight> recs = DataStore.recipeByIngredientsSorted(ings, mustIngs, request.getPage(), acc,kosher,request.getType(),request.forbiddenSites,request.getCookTimeLimit()); 
        System.out.println("server found "+recs.size()+" recipes"); 
         
    	//	System.out.println("server found "+recs.size()+" recipes");
		ClientResponse res = new ClientResponse();	
		
		for(KeyWithWeight r : recs){
//			System.out.println("SERVER: recipe name: "+r.getRecipe().getRecipeName());
//			System.out.println("SERVER: recipe type: "+r.getRecipe().getType());
			if(r.getAlsoNeed().equals("")){
			res.add(r.getRecipe().getRecipeName(),
					r.getRecipe().getKosherString(), //TODO should be string or short?
					r.getRecipe().getType(),
					r.getRecipe().getLink(),
					"יש הכל!");
			}
			else{
				res.add(r.getRecipe().getRecipeName(),
						r.getRecipe().getKosherString(), 
						r.getRecipe().getType(),
						r.getRecipe().getLink(),
						r.getAlsoNeed());
			}
		}

		return res;
	}

 

	/** @pre the ingredients name in request must be valid, or מקרר */
	private List<Key> Strings2Ingredients(ClientRequest request,Accounts acc){
		List<Key> results = new ArrayList<Key>();
		for(String ingName : request){
			if(ingName.equals("מקרר")) {
				List<Key> fridge = acc.getMyFridgeKeys();
				for(Key k : fridge) {
					if(!results.contains(k)) results.add(k);
				}
			}
			results.add(KeyFactory.createKey(Ingredient.class.getSimpleName(), ingName));
		}
		return results;
	}
	
	/** @pre the ingredients name in request must be valid */
	private List<Key> getMustIngs(ClientRequest request,List<Key> oks){
		List<Key> results = new ArrayList<Key>();
		for(String ingName : request.mustIngs){
			Key k = KeyFactory.createKey(Ingredient.class.getSimpleName(), ingName);
			if(oks.contains(k)) results.add(k);
		}
		return results;
	}
	
	
	/** returns a list of number of recipes, 5 most popular recipes, and all the ingredient names */
	public List<String> getInitData(){
		System.out.println("grt: getInitData");
		
		// INITIALIZING THE DATASTORE - this is here only for debugging.
        if(Debug.DO==Debug.RESTART || Debug.DO==Debug.SCAN_REC || Debug.DO==Debug.MEIR || Debug.DO==Debug.SCAN_MORE_REC) 
                new ServerInit().doLogic(); //IMPORTANT: put this line in if you want to do more scans! 
		
		Globals glb;
		List<String> res = new ArrayList<String>();
		
		//add number of recipes
		res.add(String.valueOf((glb=DataStore.getGlobals()).getNumOfRecipesInDS()));
		DataStore.updateGlobals(glb);
		res.addAll(glb.getMostViewed());
		
		//add all ing names
		IngredientsDB ingDB = IngredientsDB.getInstance();
		res.addAll(ingDB.getAllIngredients());
		return res;
	}
	
	public RecipeDetails getRecipeInfo(String link) {
		System.out.println("Server: getRecipeInfo: recieved <"+link+">");
		Recipes r = DataStore.getRecipeFromKey(KeyFactory.createKey(Recipes.class.getSimpleName(),link),true);
		if(r==null)
			return new RecipeDetails();
		
		//check and update popular
		Globals globals = DataStore.getGlobals();
		if(globals.getWeekNumber()==null) globals.initPopularRecipesArrays();
		System.out.println("grt: glbweek,recweek= "+ globals.getWeekNumber()+r.getWeekNumber());
		if(globals.getWeekNumber()!=r.getWeekNumber()) {
			DataStore.resetRecipeViews(r.getKey(),globals.getWeekNumber());
			if(globals.checkAndUpdateViews(r.getRecipeName()+"@"+r.getLink(),1))
				DataStore.updateGlobals(globals);
		}
		else if(globals.checkAndUpdateViews(r.getRecipeName()+"@"+r.getLink(),r.getNumOfViews()))
			DataStore.updateGlobals(globals);
		
		
		System.out.println("grt: current: "+ r.getLink() + " "+ r.getNumOfViews());
		for(int x=0 ; x<5 ; x++) {
			System.out.println(globals.getMostViewed().get(x)+" "+globals.getMostViewedNumber().get(x));
		}
		
		
		//give user what he needs
		RecipeDetails ri = new RecipeDetails(r.getInstructionsAsString(),r.getIngredientsTextAsString(),r.getGrade(),r.getAvarageCookTime());
		if(r.getComments()!=null)
			ri.addComments(r.getComments());
		IngredientsDB ingDB = IngredientsDB.getInstance();
		for(Key k : r.getIngredients()){
			ri.addIng(k.getName());
			ri.addPic(ingDB.getPicture(k,r.getKosher()));
		}

		
		
		UserService userService = UserServiceFactory.getUserService(); 
		User user = userService.getCurrentUser();
		if(user!=null) 
			DataStore.addAccountViewedRecipes(user.getEmail(), r.getLink(), r.getRecipeName());
		return ri;
	}

	/**
	 * @return 1 to 5 = the new rate after successfully rating.<br>
	 *  -1 to -5 = the old rate of the user, who can't re-rate.<br>
	 *  0 = the user is not logged in. */
	public Integer getNewGrade(String recipeName, int grade) {
		System.out.println("Server: getNewGrade: recieved <"+recipeName+">");
		UserService userService = UserServiceFactory.getUserService(); 
		User user = userService.getCurrentUser();
		if (user == null) return 0;
		int pr = DataStore.addAccountRankings(user.getEmail(), recipeName, grade);
		if(pr!=0) return (0-pr);
		return DataStore.updateRecipeGrade(recipeName, grade);
	}
	
	/**
	 * @return The new average Cook Time in minutes.
	 * or -1 if user already entered a time.
	 * */
	public Integer getNewCookTime(String recipeName, int cookTime) {
		System.out.println("Server: getNewCookTime: recieved <"+recipeName+">   " + cookTime);
		UserService userService = UserServiceFactory.getUserService(); 
		User user = userService.getCurrentUser();
		if (user == null) return 0;
		return DataStore.updateRecipeCookTime(recipeName, cookTime, user.getEmail());
	}
	
	/** add a new alternative ingredient by our logged-in users
	 * @return 1 for success,<br> 
	 * 0 if the user is not logged in<br>
	 * -1 if the recipe is not valid (meat+dairy=fail) */
	public Integer addAlterToRecipe(String link, String original, String alter){
		UserService userService = UserServiceFactory.getUserService(); 
		User user = userService.getCurrentUser();
		if(user==null) return 0;
		boolean bol=DataStore.addAlterToRecipe(KeyFactory.createKey(Recipes.class.getSimpleName(),link), KeyFactory.createKey(Ingredient.class.getSimpleName(), original), KeyFactory.createKey(Ingredient.class.getSimpleName(), alter));
		if(bol) return 1;
		else return -1;
	}
	
	/** add a new recipe to our site by our logged-in users
	 * @return 1 for success,<br> 
	 * 0 if the user is not logged in<br>
	 * -1 if the recipe is not valid (meat+dairy=fail) */
	public Integer sendNewRecipe(String recipeName, String instructions, String ingredients, String type){
		UserService userService = UserServiceFactory.getUserService(); 
		User user = userService.getCurrentUser();
		if(user==null) return 0;
		Recipes rec = new Recipes(recipeName, instructions, ingredients,type,user.getNickname());
		if(rec.isValid()) return 1;
		else return -1;
	}
	
	public List<String> getUserFridgeIngredients(){
		UserService userService = UserServiceFactory.getUserService(); 
		User user = userService.getCurrentUser();
		if (user != null){
			Accounts acc = DataStore.getAccountOfUser(user);
			ArrayList<String> ans = new ArrayList<String>();
			if (acc.getUserFridgeReq())
				ans.add("t");
			else
				ans.add("f");
	//		updateMyFridge(acc.getMyFridge());
			System.out.println("server: is user");
			System.out.println("GSimp: user mail = "+acc.getUserMail() + ", has fridge size = "+ acc.getMyFridge().size());
			ans.addAll(acc.getMyFridge());
			return ans;
		}
		return null;
	}
	
	/** get user's history in a format of list of strings of: name +"@" + link*/
	public List<String> getUserViewdedRecipes(){
		System.out.println("enter getUserViewdedRecipes");
		UserService userService = UserServiceFactory.getUserService(); 
		User user = userService.getCurrentUser();
		if (user != null) {
			Accounts acc = DataStore.getAccountOfUser(user);
			System.out.println("history size= "+acc.getHistory().size());
			return acc.getHistory();
		}
		return null;
	}
	
	/** get user's favorites in a format of list of strings of: name +"@" + link*/
	public List<String> getUserFavorites(){
		System.out.println("enter getUserFavorites");
		UserService userService = UserServiceFactory.getUserService(); 
		User user = userService.getCurrentUser();
		if (user != null) {
			Accounts acc = DataStore.getAccountOfUser(user);
			System.out.println("Favorites size= "+acc.getFavorites_name().size());
			return acc.getFavoriteRecipes();
		}
		return null;
	}
	
	/** add a recipe to user's favorites
	 * @return 1 for success,<br> 
	 * 0 if the user is not logged in<br>
	 * -1 if the recipe was already in fav */
	public Integer addUserFavorites(String rec_link){
		System.out.println("enter addUserFavorites");
		UserService userService = UserServiceFactory.getUserService(); 
		User user = userService.getCurrentUser();
		boolean bol;
		if (user != null) {
			Key k = KeyFactory.createKey(Recipes.class.getSimpleName(), rec_link);
			Recipes rec = DataStore.getRecipeFromKey(k,false);
			bol = DataStore.addAccountFavoriteRecipes(user.getEmail(), rec_link, rec.getRecipeName(), rec.getUploader());
			if(bol) return 1;
			return -1;
		}
		return 0;
	}
	
	/** delete a recipe from user's favorites
	 * @return true for success, false if not logged in */
	public Boolean deleteUserFavorites(String rec_link){
		System.out.println("enter deleteUserFavorites");
		UserService userService = UserServiceFactory.getUserService(); 
		User user = userService.getCurrentUser();
		if (user != null) {
			Key k = KeyFactory.createKey(Recipes.class.getSimpleName(), rec_link);
			Recipes rec = DataStore.getRecipeFromKey(k,false);
			DataStore.removeAccountFavoriteRecipes(user.getEmail(), rec_link, rec.getUploader());
			return true;
		}
		return false;
	}
	
	public Boolean addComment(String link, String comment) {
		System.out.println("enter addComment");
		UserService userService = UserServiceFactory.getUserService(); 
		User user = userService.getCurrentUser();
		if (user != null) {
			DataStore.addCommentToRecipe(link,comment,user.getEmail());
			return true;
		}
		return false;
	}
	
	public String logIn(){
		UserService userService = UserServiceFactory.getUserService(); 
		User user = userService.getCurrentUser();
		if (user == null){
			return userService.createLoginURL(Debug.LOGIN_LINK,"googleLoggedin.jsp");
			//return userService.createLoginURL("http://127.0.0.1:8888/Client.html?gwt.codesvr=127.0.0.1:9997","googleLoggedin.jsp");
			//return userService.createLoginURL("http://matkonkasher.appspot.com","googleLoggedin.jsp");
	
		}
		return null;
	}
	
	public String logOut(){
		UserService userService = UserServiceFactory.getUserService(); 
		User user = userService.getCurrentUser();
		if (user != null)
			return userService.createLogoutURL(Debug.LOGIN_LINK,"googleLoggedin.jsp");
			//return userService.createLogoutURL("http://127.0.0.1:8888/Client.html?gwt.codesvr=127.0.0.1:9997","googleLoggedin.jsp");
			//return userService.createLogoutURL("http://matkonkasher.appspot.com","googleLoggedin.jsp");

		return null;
	}

	
	public String isUser() {
		UserService userService = UserServiceFactory.getUserService(); 
		User user = userService.getCurrentUser();
		if (user != null)
			return user.getNickname(); 
		return "no";
	}
	
	/** update the user fridge and save his request */
	/** @pre all ingredient names are legal and passed the client test for availability*/
	public Boolean updateMyFridge(List<String> newList, boolean req) {
		System.out.println("enter update with size of "+newList.size());
		UserService userService = UserServiceFactory.getUserService(); 
		User user = userService.getCurrentUser();
		//remove duplicates
		Set<String> s = new HashSet<String>();
		s.addAll(newList);
		newList.clear();
		newList.addAll(s);
		
		if (user != null){
			DataStore.updateAccountFridge(user.getEmail(), newList);
			DataStore.setUserFridgeRequest(user.getEmail(), req);
			return true;
			}
		return false;
	}
	
	/** delete a recipe from DS. used by admins
	 * @note the function in DS works, but not sure about return value and if to check for logging
	 * @return true for success, false if not logged in */
	public Boolean deleteRecipe(String rec_link){
		System.out.println("enter deleteRecipe "+rec_link);
		UserService userService = UserServiceFactory.getUserService(); 
		User user = userService.getCurrentUser();
		if (user != null) {			
			DataStore.deleteRecipe(rec_link);
			return true;
		}
		return false;
	}

	/** send Email to a person with a recipe you choose and comments
	 * return true for success, false otherwise */
	public Boolean sendMail(String desMail, String content){
		UserService userService = UserServiceFactory.getUserService(); 
		User user = userService.getCurrentUser();
		if (user != null){
			Accounts acc = DataStore.getAccountOfUser(user);
			String senderMail = acc.getUserMail();
			System.out.println("server: trying to send messege from: "+senderMail);
			
	        Properties props = new Properties();
	        
	        Session session = Session.getDefaultInstance(props, null);

	        try {
	            Message msg = new MimeMessage(session);
	            msg.setFrom(new InternetAddress(senderMail));
	            msg.addRecipient(Message.RecipientType.TO, new InternetAddress(desMail));
	            String subject = ("כושר-קוק - המלצה על מתכון : ");
	            int first = content.indexOf('@',0);
	            int second = content.indexOf('@',first+1);
	            subject = subject.concat(content.substring(0, first));           
	            msg.setSubject(MimeUtility.encodeText(subject, "UTF-8", "Q"));           
	                       
	            String firstPart;
	            String secondPart;
	            
	            if (first == second-1){
	            	firstPart = ("חבר ממליץ לך על המתכון");
	            }
	            else
	            	firstPart  = content.substring(first+1,second);
	            
	            secondPart = content.substring(second+1);
	            secondPart = "היכנס לאתר" + " : " + secondPart + " , " + "על מנת לראות את המתכון";
  
	            msg.setText(firstPart + "\n\n" + secondPart);  
	            Transport.send(msg);

	        } catch (AddressException e) {
	        	System.out.println("SERVER: failed sending messege, Address problem: "+e.getMessage());
	        	return false;
	        } catch (MessagingException e) {
	        	System.out.println("SERVER: failed sending messege, Messege problem: "+e.getMessage());
	        	return false;
	        } catch (UnsupportedEncodingException e) {
				// TODO Auto-generated catch block
	        	System.out.println("SERVER: failed sending messege, Encoding problem: ");
	        	e.printStackTrace();
	        	return false;
			}	
		}
		System.out.println("SERVER: messege sent");
		return true;
	}
}